
#include "flashDriver.h"

void (*FlashDelay)(unsigned short n) = (void (*)(unsigned short))&rt_thread_mdelay;


static void spi_config(void)
{
    LL_SPI_InitTypeDef SPI_InitStruct = {0};

    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* Enable clock */
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    /** SPI1 Pin config
    PA5   ------> SPI1_SCK
    PA6   ------> SPI1_MISO
    PA7   ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* SPI1 parameter configuration*/
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;
    SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly = 0x0U;
    LL_SPI_Init(SPI1, &SPI_InitStruct);

    LL_SPI_Enable(SPI1);

	// wp and hold pin
	GPIO_InitStruct.Pin = LL_GPIO_PIN_2 | LL_GPIO_PIN_3;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
	LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_2);
	LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_3);
}

uint8_t sendCMD(uint8_t byte){
    /* loop while data register in not emplty */
    while (0 == LL_SPI_IsActiveFlag_TXE(SPI1))
        ;

    /* send byte through the SPI0 peripheral */
    LL_SPI_TransmitData8(SPI1, byte);

    /* wait to receive a byte */
    while (0 == LL_SPI_IsActiveFlag_RXNE(SPI1))
        ;

    /* return the byte read from the SPI bus */
    return LL_SPI_ReceiveData8(SPI1);  
}




uint32_t getFlashID(void){
	uint32_t ret[3];
	uint32_t R;
	Chip_Select();
	sendCMD(JEDEC_ID_CMD);
	sendCMD(DUMMY_BYTE);
	ret[0] = (uint32_t) sendCMD(DUMMY_BYTE);
	ret[1] = (uint32_t) sendCMD(DUMMY_BYTE);
	ret[2] = (uint32_t) sendCMD(DUMMY_BYTE);
	R = (ret[0] << 16) | (ret[1] << 8) | ret[2];
	FlashDelay(100);
	Chip_Deselect();
	return R;
}


uint8_t checkStatusReg(uint8_t status_addr){
	uint32_t ret;
	Chip_Select();
	sendCMD(READ_STATUS_REG_CMD);
	sendCMD(status_addr);
	ret = sendCMD(DUMMY_BYTE);
	FlashDelay(100);
	Chip_Deselect();
	return ret;
}


void BUFdisable(void){
	uint8_t tempConfReg;
	waitForNotBusy();
	tempConfReg = checkStatusReg(CONF_REG_ADDRESS);
	tempConfReg &= ~(1 << 3);
	Chip_Select();
	sendCMD(WRITE_STATUS_REG_CMD);
	sendCMD(CONF_REG_ADDRESS);
	sendCMD(tempConfReg);
	FlashDelay(100);
	Chip_Deselect();
}


void waitForNotBusy(void){
	uint8_t BusyBitCheck;
	do{
		statReg = checkStatusReg(STATUS_REG_ADDRESS);
		BusyBitCheck = (statReg & 0x01);
	}while(BusyBitCheck == 0x01);
}


void waitForNotWEL(void){
	uint8_t WELBitCheck;
	do{
		statReg = checkStatusReg(STATUS_REG_ADDRESS);
		WELBitCheck = (statReg & 0x02);
	}while(WELBitCheck == 0x02);
}


void resetFlash(void){
	waitForNotBusy();
	Chip_Select();
	sendCMD(DEVICE_RESET_CMD);
	FlashDelay(100);
	Chip_Deselect();
}


void enableWriteFlash(void){
	waitForNotBusy();
	Chip_Select();
	sendCMD(WRITE_ENABLE_CMD);
	Chip_Deselect();
	do{
		statReg = checkStatusReg(STATUS_REG_ADDRESS);
	}while((statReg & 0x02) != 0x02);
}


void initFlash(void){
	spi_config();
	resetFlash();
	flashID = getFlashID();
	protReg = checkStatusReg(PROT_REG_ADDRESS);
	confReg = checkStatusReg(CONF_REG_ADDRESS);
	statReg = checkStatusReg(STATUS_REG_ADDRESS);
}


void blockErase(uint32_t addr){
	uint16_t page_addr = ADDR_TO_PAGE(addr);
	waitForNotBusy();
	enableWriteFlash();
	Chip_Select();
	sendCMD(BLOCK_ERASE_CMD);
	sendCMD(DUMMY_BYTE);
	sendCMD((page_addr & 0xFF00) >> 8);
	sendCMD(page_addr & 0xFF);
	Chip_Deselect();
	waitForNotWEL();
}


uint8_t writeFlashByte(uint8_t byte, uint16_t addr){
	uint16_t column_addr = ADDR_TO_COLUMN(addr);
	uint16_t page_addr = ADDR_TO_PAGE(addr);
	waitForNotBusy();
	enableWriteFlash();
	waitForNotBusy();
	Chip_Select();
	sendCMD(PROGRAM_DATA_LOAD_CMD);
	sendCMD((column_addr & 0xFF00) >> 8);
	sendCMD(column_addr & 0xFF);
	sendCMD(byte);
	Chip_Deselect();

	waitForNotBusy();
	Chip_Select();
	sendCMD(PROGRAM_EXECUTE_CMD);
	sendCMD(DUMMY_BYTE);
	sendCMD((page_addr & 0xFF00) >> 8);
	sendCMD(page_addr & 0xFF);
	Chip_Deselect();
	waitForNotWEL();

	return HAL_OK;
}


uint8_t readFlashByte(uint16_t addr){
	uint8_t retByte;
	uint16_t column_addr = ADDR_TO_COLUMN(addr);
	uint16_t page_addr = ADDR_TO_PAGE(addr);
	waitForNotBusy();
	Chip_Select();
	sendCMD(PAGE_DATA_READ_CMD);
	sendCMD((page_addr & 0xFF00) >> 8);
	sendCMD(page_addr & 0xFF);
	Chip_Deselect();

	waitForNotBusy();
	Chip_Select();
	sendCMD(FAST_READ_CMD);
	sendCMD((column_addr & 0xFF00) >> 8);
	sendCMD(column_addr & 0xFF);
	sendCMD(DUMMY_BYTE);
	retByte = sendCMD(DUMMY_BYTE);
	Chip_Deselect();

	return retByte;
}


uint8_t writeFlashPage(uint8_t *buf, uint32_t addr, uint16_t buf_len){
    int i;
	uint16_t column_addr = ADDR_TO_COLUMN(addr);
	uint16_t page_addr = ADDR_TO_PAGE(addr);
	waitForNotBusy();
	enableWriteFlash();
	waitForNotBusy();
	Chip_Select();
	sendCMD(PROGRAM_DATA_LOAD_CMD);
	sendCMD((column_addr & 0xFF00) >> 8);
	sendCMD(column_addr & 0xFF);
	for(i=0;i<buf_len;i++){
		sendCMD(buf[i]);
	}
	Chip_Deselect();

	waitForNotBusy();
	Chip_Select();
	sendCMD(PROGRAM_EXECUTE_CMD);
	sendCMD(DUMMY_BYTE);
	sendCMD((page_addr & 0xFF00) >> 8);
	sendCMD(page_addr & 0xFF);
	Chip_Deselect();
	waitForNotWEL();

    return HAL_OK;
}


uint8_t readFlashPage(uint8_t *buf, uint32_t addr, uint16_t buf_len){
    int i;
	uint16_t column_addr = ADDR_TO_COLUMN(addr);
	uint16_t page_addr = ADDR_TO_PAGE(addr);
	waitForNotBusy();
	Chip_Select();
	sendCMD(PAGE_DATA_READ_CMD);
	sendCMD((page_addr & 0xFF00) >> 8);
	sendCMD(page_addr & 0xFF);
	Chip_Deselect();

	waitForNotBusy();
	Chip_Select();
	sendCMD(FAST_READ_CMD);
	sendCMD((column_addr & 0xFF00) >> 8);
	sendCMD(column_addr & 0xFF);
	sendCMD(DUMMY_BYTE);
	for(i=0;i<buf_len;i++){
		buf[i] = sendCMD(DUMMY_BYTE);
	}
	Chip_Deselect();

    return HAL_OK;
}





